package com.yingxin.yxerp.module.activiti.biz.manager;

import com.alibaba.fastjson.JSON;
import com.yingxin.yx.framework.commons.activiti.ApprovalStatus;
import com.yingxin.yx.framework.commons.activiti.def.TaskDef;
import com.yingxin.yx.framework.commons.activiti.dto.AuditDto;
import com.yingxin.yx.framework.commons.activiti.enums.AuditAction;
import com.yingxin.yx.framework.commons.activiti.enums.FormKey;
import com.yingxin.yx.framework.commons.constant.CommonConst;
import com.yingxin.yx.framework.commons.enums.BusinessType;
import com.yingxin.yx.framework.commons.enums.PcOrAppEnums;
import com.yingxin.yx.framework.commons.exception.BizException;
import com.yingxin.yx.framework.commons.helper.ListHelper;
import com.yingxin.yx.framework.redis.lock.RedisLockSupport;
import com.yingxin.yx.framework.user.core.IUserSupport;
import com.yingxin.yxerp.module.activiti.api.BizProcessHandler;
import com.yingxin.yxerp.module.activiti.entity.ActivitiBizInfo;
import com.yingxin.yxerp.module.activiti.service.ActivitiProcessService;
import com.yingxin.yxerp.module.activiti.service.IActivitiBizInfoService;
import com.yingxin.yxerp.module.attachment.service.IAttachmentService;
import com.yingxin.yxerp.module.common.BusinessTypeHelper;
import com.yingxin.yxerp.module.message.constant.UserMsgType;
import com.yingxin.yxerp.module.message.entity.UserMessage;
import com.yingxin.yxerp.module.message.service.IUserMessageService;
import com.yingxin.yxerp.module.oplog.aop.OplogAnnotation;
import com.yingxin.yxerp.module.organization.entity.User;
import com.yingxin.yxerp.module.organization.service.IUserService;
import com.yingxin.yxerp.module.print.service.IPrintBusinessStatService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Map;

@Service
public class AuditService {

    @Autowired
    private IUserService userService;

    @Autowired
    private RedisLockSupport redisLockSupport;

    @Autowired
    private IActivitiBizInfoService activitiBizInfoService;

    @Autowired
    private IUserMessageService userMessageService;

    @Autowired
    private IUserSupport userSupport;

    @Autowired
    private IAttachmentService attachmentService;

    @Autowired
    private ActivitiProcessService activitiProcessService;

    @Autowired
    private CarbonCopyService carbonCopyService;

    @Autowired
    private BizProcessHandlerRouter bizProcessHandlerRouter;

    @Autowired
    private IPrintBusinessStatService printBusinessStatService;

    @Autowired
    private ProcessEngine processEngine;

    /**
     * approved
     */
    @OplogAnnotation(biz = BusinessType.NONE, id = "{{auditDto.businessId}}", bizCode = "{{auditDto.businessType}}",
            action = "审核[流程状态：{{auditDto.approvalStatusName}}]", desc = "{{auditDto.businessNo}}，流程状态：{{auditDto.approvalStatusName}}")
    @Transactional(rollbackFor = RuntimeException.class)
    public void approved(AuditDto auditDto) {
        if (StringUtils.isBlank(auditDto.getTaskId())) {
            throw new BizException("taskId 不能为空");
        }
        try {
            redisLockSupport.lock(auditDto.getTaskId());
            // task
            Task task = activitiProcessService.getTaskById(auditDto.getTaskId());
            if (task == null) {
                throw new BizException("审批任务不存在，请刷新再试");
            }

            // processInstance
            ProcessInstance processInstance = activitiProcessService.getProcessInstanceById(task.getProcessInstanceId());
            auditDto.setProcessDefinitionId(processInstance.getProcessDefinitionId());
            auditDto.setProcessInstanceId(processInstance.getProcessInstanceId());

            // bizInfo
            ActivitiBizInfo bizInfo = activitiBizInfoService.getById(task.getBusinessKey());
            auditDto.addBizInfo(bizInfo.getBizId(), bizInfo.getBizNo(), bizInfo.getBizType());
            auditDto.setAction(CommonConst.TRUE.equals(auditDto.getFinalFlag()) ? AuditAction.FINAL_APPROVE : AuditAction.APPROVE);

            // 任务定义
            List<TaskDef> taskDefList = JSON.parseArray(bizInfo.getBpmnTask(), TaskDef.class);
            Map<String, TaskDef> taskDefMap = ListHelper.toMap(TaskDef::getElementId, taskDefList);
            TaskDef taskDef = taskDefMap.get(task.getTaskDefinitionKey());
            auditDto.setIncomeSubtract(taskDef.getIncomeSubtract());
            auditDto.setIsAppApproval(taskDef.getIsAppApproval());
            auditDto.setCustomFormId(taskDef.getCustomFormId());

            // 验证是pc端时，是否可pc端审核
            if( StringUtils.isNotEmpty( auditDto.getIsAppApproval() ) ){
                if( auditDto.getPcOrApp().equals( PcOrAppEnums.PC.getType() ) ){
                    if( auditDto.getIsAppApproval().equals( CommonConst.TRUE ) ){
                        throw new BizException("请在APP端审核！");
                    }
                }
            }

            // BizProcessHandler
            BizProcessHandler bizHandler = bizProcessHandlerRouter.getProcessHandler(
                    processInstance.getProcessDefinitionKey() );

            // variables
            auditDto.setVariables(bizHandler.getVariables(bizInfo.getBizId()));

            // approved
            String userId = userSupport.getUserId();
            User user = userService.getById(userId);
            ApprovalStatus approvalStatus = activitiProcessService.approved(auditDto, user, taskDef, bizInfo);
            auditDto.setApprovalStatus(approvalStatus.getStatus());

            // afterAudit
            bizHandler.doCommonBiz(bizInfo.getBizId(), auditDto, approvalStatus);
            bizHandler.doTaskApprovedBiz(bizInfo.getBizId(), auditDto, approvalStatus);
            if (ApprovalStatus.APPROVED.equals(approvalStatus)) {
                bizHandler.doProcessApprovedBiz(bizInfo.getBizId(), auditDto, approvalStatus);
            }

            // 抄送
            carbonCopyService.creatCarbonCopy(processInstance, task, bizInfo, approvalStatus);

            // 附件
            attachmentService.addInfo(bizInfo.getBizId(), BusinessType.AUDIT_TASK, task.getId(), auditDto.getTaskAttachmentList(), Boolean.FALSE);

            // UserMessage
            UserMessage message = new UserMessage(bizInfo.getBizId(), bizInfo.getBizNo(), bizInfo.getBizType());
            message.setMessageType(UserMsgType.SP_A.getType());
            message.setProcessInstanceId(processInstance.getId());
            message.setTaskId(task.getId());
            message.setMessageContent(String.format("[%s-%s]被[%s]审核，当前状态[%s]", BusinessTypeHelper.getName(bizInfo.getBizType()),
                    bizInfo.getBizNo(), user.getStaffName(), approvalStatus.getStatusName()));
            userMessageService.create(message, userId, processInstance.getStartUserId());

            // 判断是否可以打印
            if (FormKey.PRINT_LIMIT.getKey().equals(auditDto.getFormKey()) || ApprovalStatus.APPROVED.equals(approvalStatus)) {
                printBusinessStatService.updatePrintStatusByApproved(processInstance.getBusinessKey());
            }
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } finally {
            redisLockSupport.unlock(auditDto.getTaskId());
        }
    }

    /**
     * unapproved
     */
    @OplogAnnotation(biz = BusinessType.NONE, id = "{{auditDto.businessId}}", bizCode = "{{auditDto.businessType}}",
            action = "审核拒绝", desc = "{{auditDto.businessNo}}，审核状态：{{auditDto.approvalStatusName}}")
    @Transactional(rollbackFor = RuntimeException.class)
    public void unapproved(AuditDto auditDto) {
        if (StringUtils.isBlank(auditDto.getTaskId())) {
            throw new BizException("taskId 不能为空");
        }
        if (StringUtils.isBlank(auditDto.getComment())) {
            throw new BizException("审核不通过原因不能为空");
        }
        try {
            redisLockSupport.lock(auditDto.getTaskId());

            // task
            Task task = activitiProcessService.getTaskById(auditDto.getTaskId());
            if (task == null) {
                throw new BizException("审批任务不存在，请刷新再试");
            }

            // processInstance
            ProcessInstance processInstance = activitiProcessService.getProcessInstanceById(task.getProcessInstanceId());

            // bizInfo
            ActivitiBizInfo bizInfo = activitiBizInfoService.getById(task.getBusinessKey());
            auditDto.addBizInfo(bizInfo.getBizId(), bizInfo.getBizNo(), bizInfo.getBizType());
            auditDto.setAction(AuditAction.REJECT);

            // 任务定义
            List<TaskDef> taskDefList = JSON.parseArray(bizInfo.getBpmnTask(), TaskDef.class);
            Map<String, TaskDef> taskDefMap = ListHelper.toMap(TaskDef::getElementId, taskDefList);
            TaskDef taskDef = taskDefMap.get(task.getTaskDefinitionKey());
            auditDto.setIncomeSubtract(taskDef.getIncomeSubtract());
            auditDto.setIsAppApproval(taskDef.getIsAppApproval());

            // 验证是pc端时，是否可pc端审核
            if( StringUtils.isNotEmpty( auditDto.getIsAppApproval() ) ){
                if( auditDto.getPcOrApp().equals( PcOrAppEnums.PC.getType() ) ){
                    if( auditDto.getIsAppApproval().equals( CommonConst.TRUE ) ){
                        throw new BizException("请在APP端审核！");
                    }
                }
            }

            // unapproved
            String userId = userSupport.getUserId();
            User user = userService.getById(userId);
            ApprovalStatus approvalStatus = activitiProcessService.unapproved(auditDto, user, taskDef);
            auditDto.setApprovalStatus(approvalStatus.getStatus());

            // afterAudit
            BizProcessHandler bizHandler = bizProcessHandlerRouter.getProcessHandler(processInstance.getProcessDefinitionKey());
            bizHandler.doCommonBiz(bizInfo.getBizId(), auditDto, approvalStatus);
            bizHandler.doProcessUnapprovedBiz(bizInfo.getBizId(), auditDto, approvalStatus);

            // 附件
            attachmentService.addInfo(bizInfo.getBizId(), BusinessType.AUDIT_TASK, task.getId(), auditDto.getTaskAttachmentList(), Boolean.FALSE);

            // UserMessage
            UserMessage message = new UserMessage(bizInfo.getBizId(), bizInfo.getBizNo(), bizInfo.getBizType());
            message.setMessageType(UserMsgType.SP_R.getType());
            message.setProcessInstanceId(processInstance.getId());
            message.setTaskId(task.getId());
            message.setMessageContent(String.format("[%s-%s]被[%s]拒绝，当前状态[%s]", BusinessTypeHelper.getName(bizInfo.getBizType()),
                    bizInfo.getBizNo(), user.getStaffName(), approvalStatus.getStatusName()));
            userMessageService.create(message, userId, processInstance.getStartUserId());

        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } finally {
            redisLockSupport.unlock(auditDto.getTaskId());
        }
    }

    /**
     * 强制拒绝审核
     */
    @OplogAnnotation(biz = BusinessType.NONE, id = "{{auditDto.businessId}}", bizCode = "{{auditDto.businessType}}",
            action = "admin强制审核", desc = "{{auditDto.businessNo}}，审核状态：{{auditDto.approvalStatusName}}")
    @Transactional(rollbackFor = RuntimeException.class)
    public void forceApproved(AuditDto auditDto) {
        if (StringUtils.isBlank(auditDto.getTaskId())) {
            throw new BizException("taskId 不能为空");
        }
        if (StringUtils.isBlank(auditDto.getComment())) {
            throw new BizException("审核不通过原因不能为空");
        }
        try {
            redisLockSupport.lock( auditDto.getTaskId() );

            // task
            Task task = activitiProcessService.getTaskById(auditDto.getTaskId());
            if (task == null) {
                throw new BizException("审批任务不存在，请刷新再试");
            }

            // processInstance
            ProcessInstance processInstance = activitiProcessService.getProcessInstanceById(task.getProcessInstanceId());

            // bizInfo
            ActivitiBizInfo bizInfo = activitiBizInfoService.getById(task.getBusinessKey());
            auditDto.addBizInfo(bizInfo.getBizId(), bizInfo.getBizNo(), bizInfo.getBizType());
            auditDto.setAction(AuditAction.REJECT);

            // 任务定义
            List<TaskDef> taskDefList = JSON.parseArray(bizInfo.getBpmnTask(), TaskDef.class);
            Map<String, TaskDef> taskDefMap = ListHelper.toMap(TaskDef::getElementId, taskDefList);
            TaskDef taskDef = taskDefMap.get(task.getTaskDefinitionKey());
            auditDto.setIncomeSubtract(taskDef.getIncomeSubtract());
            auditDto.setIsAppApproval(taskDef.getIsAppApproval());

            // 验证是pc端时，是否可pc端审核
            if( StringUtils.isNotEmpty( auditDto.getIsAppApproval() ) ){
                if( auditDto.getPcOrApp().equals( PcOrAppEnums.PC.getType() ) ){
                    if( auditDto.getIsAppApproval().equals( CommonConst.TRUE ) ){
                        throw new BizException("请在APP端审核！");
                    }
                }
            }

            // unapproved
            String userId = userSupport.getUserId();
            User user = userService.getById( userId );
            ApprovalStatus approvalStatus = activitiProcessService.forceApproved(auditDto, user, taskDef);
            auditDto.setApprovalStatus( approvalStatus.getStatus() );

            // afterAudit
            BizProcessHandler bizHandler = bizProcessHandlerRouter.getProcessHandler(processInstance.getProcessDefinitionKey());
            bizHandler.doCommonBiz(bizInfo.getBizId(), auditDto, approvalStatus);
            bizHandler.doProcessUnapprovedBiz(bizInfo.getBizId(), auditDto, approvalStatus);

            // 附件
            attachmentService.addInfo(bizInfo.getBizId(), BusinessType.AUDIT_TASK, task.getId(), auditDto.getTaskAttachmentList(), Boolean.FALSE);

            // UserMessage
            UserMessage message = new UserMessage(bizInfo.getBizId(), bizInfo.getBizNo(), bizInfo.getBizType());
            message.setMessageType(UserMsgType.SP_R.getType());
            message.setProcessInstanceId(processInstance.getId());
            message.setTaskId(task.getId());
            message.setMessageContent(String.format("[%s-%s]被[%s]拒绝，当前状态[%s]", BusinessTypeHelper.getName(bizInfo.getBizType()),
                    bizInfo.getBizNo(), user.getStaffName(), approvalStatus.getStatusName()));
            userMessageService.create(message, userId, processInstance.getStartUserId());

        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } finally {
            redisLockSupport.unlock(auditDto.getTaskId());
        }
    }

    /**
     * 获取当前审核人id
     * @param taskId
     * @return
     */
    private String getDefUsersByTaskId( String taskId ){
        // 获取任务
        Task task = processEngine.getTaskService().createTaskQuery().taskId( taskId ).singleResult();

        // 获取流程定义id
        String processDefinitionId = task.getProcessDefinitionId();

        ProcessDefinitionEntity processDefinitionEntity = ( ProcessDefinitionEntity )
                processEngine.getRepositoryService().getProcessDefinition( processDefinitionId );

        // 根据活动id获取活动实例
        // ActivityImpl activityImpl = processDefinitionEntity.findActivity( task.getTaskDefinitionKey() );

        // TaskDefinition taskDef = (TaskDefinition)activityImpl.getProperties().get("taskDefinition");

        // 候选人
        // Set<Expression> userCodes = taskDef.getCandidateUserIdExpressions();

        return null;
    }

    /**
     * auditCancel
     */
    @OplogAnnotation(biz = BusinessType.NONE, id = "{{auditDto.businessId}}", bizCode = "{{auditDto.businessType}}",
            action = "审核被取消", desc = "{{auditDto.businessNo}}，审核状态：{{auditDto.approvalStatusName}}")
    @Transactional(rollbackFor = RuntimeException.class)
    public void auditCancel(AuditDto auditDto) {
        if (StringUtils.isBlank(auditDto.getProcessInstanceId())) {
            throw new BizException("processInstanceId 不能为空");
        }
        if (StringUtils.isBlank(auditDto.getComment())) {
            throw new BizException("撤销原因不能为空");
        }
        try {
            redisLockSupport.lock(auditDto.getProcessInstanceId());

            // processInstance
            ProcessInstance processInstance = activitiProcessService.getProcessInstanceById(auditDto.getProcessInstanceId());
            if (processInstance == null) {
                throw new BizException("审批任务不存在，请刷新再试");
            }

            // bizInfo
            ActivitiBizInfo bizInfo = activitiBizInfoService.getById(processInstance.getBusinessKey());
            auditDto.addBizInfo(bizInfo.getBizId(), bizInfo.getBizNo(), bizInfo.getBizType());
            auditDto.setAction(AuditAction.CANCEL);

            // auditCancel
            ApprovalStatus approvalStatus = activitiProcessService.auditCancel(auditDto);
            auditDto.setApprovalStatus(approvalStatus.getStatus());

            // afterAudit
            BizProcessHandler bizHandler = bizProcessHandlerRouter.getProcessHandler(processInstance.getProcessDefinitionKey());
            bizHandler.doCommonBiz(bizInfo.getBizId(), auditDto, approvalStatus);
            bizHandler.doProcessUnapprovedBiz(bizInfo.getBizId(), auditDto, approvalStatus);

        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } finally {
            redisLockSupport.unlock(auditDto.getProcessInstanceId());
        }
    }

}